﻿using System;
using System.Collections.Generic;
using System.Globalization;

using Microsoft.Extensions.Configuration;
using Unity.Environment.Exceptions;

namespace Unity.Environment
{
    public static class UnityEnvironment
    {
        private static readonly Dictionary<string, string> Parameters = InitializeCurrentEnvironment();
        private const string EnvironmentParameterNotFoundMessage =
            "";
        
        private static Dictionary<string, string> InitializeCurrentEnvironment()
        {
            Dictionary<string, string> currentParameters = new Dictionary<string, string>();
            IConfiguration currentConfiguration = LoadCurrentEnvironmentConfiguration();
            if(currentConfiguration != null)
            {
                foreach(var item in currentConfiguration.GetChildren())
                {
                    currentParameters.Add(item.Key, item.Value);
                }
            }
            return currentParameters;
        }

        public static IEnumerable<KeyValuePair<string, string>> GetAllCurrentEnvironmentParameters()
        {
            List<KeyValuePair<string, string>> currentParameters = new List<KeyValuePair<string, string>>();
            if(Parameters.Count > 0)
            {
                foreach(KeyValuePair<string, string> parameter in Parameters)
                {
                    currentParameters.Add(parameter);
                }
            }
            return currentParameters;
        }
        

        static bool TryGetBoolParameter(string key, out bool value)
        {
            var parameterValue = GetCurrentEnvironmentParameter(key);
            if (parameterValue == null)
            {
                value = false;
                return false;
            }
            value = bool.Parse(parameterValue);
            return true;
        }

        static bool TryGetIntegerParameter(string key, out int value)
        {
            var parameterValue = GetCurrentEnvironmentParameter(key);
            if (parameterValue == null)
            {
                value = 0;
                return false;
            }
            value = int.Parse(parameterValue);
            return true;
        }

        static bool TryGetDoubleParameter(string key, out double value)
        {
            var parameterValue = GetCurrentEnvironmentParameter(key);
            if(parameterValue == null)
            {
                value = 0.0;
                return false;
            }
            value = double.Parse(parameterValue, CultureInfo.InvariantCulture);
            return true;
        }

        static bool TryGetStringParameter(string key, out string value)
        {
            var parameterValue = GetCurrentEnvironmentParameter(key);
            if (parameterValue == null)
            {
                value = null;
                return false;
            }
            value = parameterValue;
            return true;
        }

        public static bool GetBoolParameter(string key)
        {
            if(TryGetBoolParameter(key, out bool value))
            {
                return value;
            }
            throw new EnvironmentParameterNotFoundException(EnvironmentParameterNotFoundMessage, nameof(key));
        }

        public static bool GetBoolParameter(UnityParameter parameter)
        {
            return GetBoolParameter(parameter.Value());
        }

        public static int GetIntegerParameter(string key)
        {
            if(TryGetIntegerParameter(key, out int value))
            {
                return value;
            }
            throw new EnvironmentParameterNotFoundException(EnvironmentParameterNotFoundMessage, nameof(key));
        }

        public static int GetIntegerParameter(UnityParameter parameter)
        {
            return GetIntegerParameter(parameter.Value());
        }

        public static double GetDoubleParameter(string key)
        {
            if(TryGetDoubleParameter(key, out double value))
            {
                return value;
            }
            throw new EnvironmentParameterNotFoundException(EnvironmentParameterNotFoundMessage, nameof(key));
        }

        public static double GetDoubleParameter(UnityParameter parameter)
        {
            return GetDoubleParameter(parameter.Value());
        }

        public static string GetStringParameter(string key)
        {
            if (TryGetStringParameter(key, out string value))
            {
                return value;
            }
            throw new EnvironmentParameterNotFoundException(EnvironmentParameterNotFoundMessage, nameof(key));
        }

        public static string GetStringParameter(UnityParameter parameter)
        {
            return GetStringParameter(parameter.Value());
        }

        static string GetCurrentEnvironmentParameter(string key)
        {
            if (string.IsNullOrWhiteSpace(key))
            {
                throw new ArgumentNullException(nameof(key), "Error get environment parameter value with null or empty key");
            }
            if(Parameters.ContainsKey(key))
            {
                return Parameters[key];
            }
            return System.Environment.GetEnvironmentVariable(key);
        }

        public static bool SetCurrentEnvironmentParameter(string key, string value)
        {
            if(string.IsNullOrWhiteSpace(key))
            {
                return false;
            }
            System.Environment.SetEnvironmentVariable(key, value);
            return true;
        }

        public static bool SetCurrentEnvironmentParameter(KeyValuePair<string, string> parameter)
        {
            return SetCurrentEnvironmentParameter(parameter.Key, parameter.Value);
        }

        private static IConfiguration LoadCurrentEnvironmentConfiguration()
        {
            IConfiguration currentConfiguration;
            try
            {
                currentConfiguration = MakeConfiguration(true, true, null);
            }
            catch (Exception exception)
            {
                throw new EnvironmentInitializationException(
                    $"Error initialization environment: {exception.Message} \n{exception.StackTrace}");
            }
            return currentConfiguration;
        }

        private static IConfiguration MakeConfiguration(bool useEnvironment, bool useArguments, string envFilePath)
        {
            var configurationBuilder = new ConfigurationBuilder();
            if(useEnvironment)
            {
                configurationBuilder.AddEnvironmentVariables();
            }
            if(useArguments)
            {
                string[] commandLineArgs = System.Environment.GetCommandLineArgs();
                configurationBuilder.AddCommandLine(commandLineArgs);
            }
            if(!string.IsNullOrWhiteSpace(envFilePath))
            {
                 // TODO loading env from .json and .xml files
            }
            return configurationBuilder.Build();
        }


    }
}
